/************************************************************
 Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.

 Permission to use, copy, modify, and distribute this
 software and its documentation for any purpose and without
 fee is hereby granted, provided that the above copyright
 notice appear in all copies and that both that copyright
 notice and this permission notice appear in supporting
 documentation, and that the name of Silicon Graphics not be
 used in advertising or publicity pertaining to distribution
 of the software without specific prior written permission.
 Silicon Graphics makes no representation about the suitability
 of this software for any purpose. It is provided "as is"
 without any express or implied warranty.

 SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
 GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
 THE USE OR PERFORMANCE OF THIS SOFTWARE.

 ********************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <X11/Xfuncs.h>
#include <X11/Xlib.h>
#include <X11/XKBlib.h>
#include <X11/extensions/XKBgeom.h>

#include "XKMformat.h"
#include "XKBfileInt.h"

typedef struct _XkmInfo {
    unsigned short bound_vmods;
    unsigned short named_vmods;
    unsigned char  num_bound;
    unsigned char  group_compat;
    unsigned short num_group_compat;
    unsigned short num_leds;
    int            total_vmodmaps;
} XkmInfo;

/***====================================================================***/

#define	xkmPutCARD8(f,v)	(putc(v,f),1)

static int
xkmPutCARD16(FILE *file, unsigned val)
{
    CARD16 tmp = val;

    fwrite(&tmp, 2, 1, file);
    return 2;
}

static int
xkmPutCARD32(FILE *file, unsigned long val)
{
    CARD32 tmp = val;

    fwrite(&tmp, 4, 1, file);
    return 4;
}

static int
xkmPutPadding(FILE *file, unsigned pad)
{
    int i;

    for (i = 0; i < pad; i++) {
        putc('\0', file);
    }
    return pad;
}

static int
xkmPutCountedBytes(FILE *file, char *ptr, unsigned count)
{
    register int nOut;
    register unsigned pad;

    if (count == 0)
        return xkmPutCARD32(file, (unsigned long) 0);

    xkmPutCARD16(file, count);
    nOut = fwrite(ptr, 1, count, file);
    if (nOut < 0)
        return 2;
    nOut = count + 2;
    pad = XkbPaddedSize(nOut) - nOut;
    if (pad)
        xkmPutPadding(file, pad);
    return nOut + pad;
}

static unsigned
xkmSizeCountedString(char *str)
{
    if (str == NULL)
        return 4;
    return XkbPaddedSize(strlen(str) + 2);
}

static int
xkmPutCountedString(FILE *file, char *str)
{
    if (str == NULL)
        return xkmPutCARD32(file, (unsigned long) 0);
    return xkmPutCountedBytes(file, str, strlen(str));
}

#define	xkmSizeCountedAtomString(d,a)	\
	xkmSizeCountedString(XkbAtomGetString((d),(a)))

#define	xkmPutCountedAtomString(d,f,a)	\
	xkmPutCountedString((f),XkbAtomGetString((d),(a)))

/***====================================================================***/

static unsigned
SizeXKMVirtualMods(XkbFileInfo *result, XkmInfo *info,
                   xkmSectionInfo *toc, int *offset_inout)
{
    Display *dpy;
    XkbDescPtr xkb;
    unsigned nBound, bound;
    unsigned nNamed, named, szNames;
    register unsigned i, bit;

    xkb = result->xkb;
    if ((!xkb) || (!xkb->names) || (!xkb->server)) {
        _XkbLibError(_XkbErrMissingVMods, "SizeXKMVirtualMods", 0);
        return 0;
    }
    dpy = xkb->dpy;
    bound = named = 0;
    for (i = nBound = nNamed = szNames = 0, bit = 1; i < XkbNumVirtualMods;
         i++, bit <<= 1) {
        if (xkb->server->vmods[i] != XkbNoModifierMask) {
            bound |= bit;
            nBound++;
        }
        if (xkb->names->vmods[i] != None) {
            named |= bit;
            szNames += xkmSizeCountedAtomString(dpy, xkb->names->vmods[i]);
            nNamed++;
        }
    }
    info->num_bound = nBound;
    info->bound_vmods = bound;
    info->named_vmods = named;
    if ((nBound == 0) && (nNamed == 0))
        return 0;
    toc->type = XkmVirtualModsIndex;
    toc->format = MSBFirst;
    toc->size = 4 + XkbPaddedSize(nBound) + szNames + SIZEOF(xkmSectionInfo);
    toc->offset = *offset_inout;
    (*offset_inout) += toc->size;
    return 1;
}

static unsigned
WriteXKMVirtualMods(FILE *file, XkbFileInfo *result, XkmInfo *info)
{
    register unsigned int i, bit;
    XkbDescPtr xkb;
    Display *dpy;
    unsigned size = 0;

    xkb = result->xkb;
    dpy = xkb->dpy;
    size += xkmPutCARD16(file, info->bound_vmods);
    size += xkmPutCARD16(file, info->named_vmods);
    for (i = 0, bit = 1; i < XkbNumVirtualMods; i++, bit <<= 1) {
        if (info->bound_vmods & bit)
            size += xkmPutCARD8(file, xkb->server->vmods[i]);
    }
    if ((i = XkbPaddedSize(info->num_bound) - info->num_bound) > 0)
        size += xkmPutPadding(file, i);
    for (i = 0, bit = 1; i < XkbNumVirtualMods; i++, bit <<= 1) {
        if (info->named_vmods & bit) {
            register char *name;

            name = XkbAtomGetString(dpy, xkb->names->vmods[i]);
            size += xkmPutCountedString(file, name);
        }
    }
    return size;
}

/***====================================================================***/

static unsigned
SizeXKMKeycodes(XkbFileInfo *result, xkmSectionInfo *toc, int *offset_inout)
{
    XkbDescPtr xkb;
    Atom kcName;
    int size = 0;
    Display *dpy;

    xkb = result->xkb;
    if ((!xkb) || (!xkb->names) || (!xkb->names->keys)) {
        _XkbLibError(_XkbErrMissingNames, "SizeXKMKeycodes", 0);
        return 0;
    }
    dpy = xkb->dpy;
    kcName = xkb->names->keycodes;
    size += 4;                  /* min and max keycode */
    size += xkmSizeCountedAtomString(dpy, kcName);
    size += XkbNumKeys(xkb) * sizeof(XkbKeyNameRec);
    if (xkb->names->num_key_aliases > 0) {
        if (xkb->names->key_aliases != NULL)
            size += xkb->names->num_key_aliases * sizeof(XkbKeyAliasRec);
        else
            xkb->names->num_key_aliases = 0;
    }
    toc->type = XkmKeyNamesIndex;
    toc->format = MSBFirst;
    toc->size = size + SIZEOF(xkmSectionInfo);
    toc->offset = (*offset_inout);
    (*offset_inout) += toc->size;
    return 1;
}

static unsigned
WriteXKMKeycodes(FILE *file, XkbFileInfo *result)
{
    XkbDescPtr xkb;
    Atom kcName;
    char *start;
    Display *dpy;
    unsigned tmp, size = 0;

    xkb = result->xkb;
    dpy = xkb->dpy;
    kcName = xkb->names->keycodes;
    start = xkb->names->keys[xkb->min_key_code].name;

    size += xkmPutCountedString(file, XkbAtomGetString(dpy, kcName));
    size += xkmPutCARD8(file, xkb->min_key_code);
    size += xkmPutCARD8(file, xkb->max_key_code);
    size += xkmPutCARD8(file, xkb->names->num_key_aliases);
    size += xkmPutPadding(file, 1);
    tmp = fwrite(start, sizeof(XkbKeyNameRec), XkbNumKeys(xkb), file);
    size += tmp * sizeof(XkbKeyNameRec);
    if (xkb->names->num_key_aliases > 0) {
        tmp = fwrite((char *) xkb->names->key_aliases,
                     sizeof(XkbKeyAliasRec), xkb->names->num_key_aliases, file);
        size += tmp * sizeof(XkbKeyAliasRec);
    }
    return size;
}

/***====================================================================***/

static unsigned
SizeXKMKeyTypes(XkbFileInfo *result, xkmSectionInfo *toc, int *offset_inout)
{
    register unsigned i, n, size;
    XkbKeyTypePtr type;
    XkbDescPtr xkb;
    Display *dpy;
    char *name;

    xkb = result->xkb;
    if ((!xkb) || (!xkb->map) || (!xkb->map->types)) {
        _XkbLibError(_XkbErrMissingTypes, "SizeXKBKeyTypes", 0);
        return 0;
    }
    dpy = xkb->dpy;
    if (xkb->map->num_types < XkbNumRequiredTypes) {
        _XkbLibError(_XkbErrMissingReqTypes, "SizeXKBKeyTypes", 0);
        return 0;
    }
    if (xkb->names)
        name = XkbAtomGetString(dpy, xkb->names->types);
    else
        name = NULL;
    size = xkmSizeCountedString(name);
    size += 4;                  /* room for # of key types + padding */
    for (i = 0, type = xkb->map->types; i < xkb->map->num_types; i++, type++) {
        size += SIZEOF(xkmKeyTypeDesc);
        size += SIZEOF(xkmKTMapEntryDesc) * type->map_count;
        size += xkmSizeCountedAtomString(dpy, type->name);
        if (type->preserve)
            size += SIZEOF(xkmModsDesc) * type->map_count;
        if (type->level_names) {
            Atom *names;

            names = type->level_names;
            for (n = 0; n < (unsigned) type->num_levels; n++) {
                size += xkmSizeCountedAtomString(dpy, names[n]);
            }
        }
    }
    toc->type = XkmTypesIndex;
    toc->format = MSBFirst;
    toc->size = size + SIZEOF(xkmSectionInfo);
    toc->offset = (*offset_inout);
    (*offset_inout) += toc->size;
    return 1;
}

static unsigned
WriteXKMKeyTypes(FILE *file, XkbFileInfo *result)
{
    register unsigned i, n;
    XkbDescPtr xkb;
    XkbKeyTypePtr type;
    xkmKeyTypeDesc wire;
    XkbKTMapEntryPtr entry;
    xkmKTMapEntryDesc wire_entry;
    Atom *names;
    Display *dpy;
    unsigned tmp, size = 0;
    char *name;

    xkb = result->xkb;
    dpy = xkb->dpy;
    if (xkb->names)
        name = XkbAtomGetString(dpy, xkb->names->types);
    else
        name = NULL;
    size += xkmPutCountedString(file, name);
    size += xkmPutCARD16(file, xkb->map->num_types);
    size += xkmPutPadding(file, 2);
    type = xkb->map->types;
    for (i = 0; i < xkb->map->num_types; i++, type++) {
        wire.realMods = type->mods.real_mods;
        wire.virtualMods = type->mods.vmods;
        wire.numLevels = type->num_levels;
        wire.nMapEntries = type->map_count;
        wire.preserve = (type->preserve != NULL);
        if (type->level_names != NULL)
            wire.nLevelNames = type->num_levels;
        else
            wire.nLevelNames = 0;
        wire.pad = 0;
        tmp = fwrite(&wire, SIZEOF(xkmKeyTypeDesc), 1, file);
        size += tmp * SIZEOF(xkmKeyTypeDesc);
        for (n = 0, entry = type->map; n < type->map_count; n++, entry++) {
            wire_entry.level = entry->level;
            wire_entry.realMods = entry->mods.real_mods;
            wire_entry.virtualMods = entry->mods.vmods;
            tmp = fwrite(&wire_entry, SIZEOF(xkmKTMapEntryDesc), 1, file);
            size += tmp * SIZEOF(xkmKTMapEntryDesc);
        }
        size += xkmPutCountedString(file, XkbAtomGetString(dpy, type->name));
        if (type->preserve) {
            xkmModsDesc p_entry;

            XkbModsPtr pre;

            for (n = 0, pre = type->preserve; n < type->map_count; n++, pre++) {
                p_entry.realMods = pre->real_mods;
                p_entry.virtualMods = pre->vmods;
    p_entry.pad = 0;
                tmp = fwrite(&p_entry, SIZEOF(xkmModsDesc), 1, file);
                size += tmp * SIZEOF(xkmModsDesc);
            }
        }
        if (type->level_names != NULL) {
            names = type->level_names;
            for (n = 0; n < wire.nLevelNames; n++) {
                size +=
                    xkmPutCountedString(file, XkbAtomGetString(dpy, names[n]));
            }
        }
    }
    return size;
}

/***====================================================================***/

static unsigned
SizeXKMCompatMap(XkbFileInfo *result, XkmInfo *info,
                 xkmSectionInfo *toc, int *offset_inout)
{
    XkbDescPtr xkb;
    char *name;
    int size;
    register int i;
    unsigned groups, nGroups;
    Display *dpy;

    xkb = result->xkb;
    if ((!xkb) || (!xkb->compat) || (!xkb->compat->sym_interpret)) {
        _XkbLibError(_XkbErrMissingCompatMap, "SizeXKMCompatMap", 0);
        return 0;
    }
    dpy = xkb->dpy;
    if (xkb->names)
        name = XkbAtomGetString(dpy, xkb->names->compat);
    else
        name = NULL;

    for (i = groups = nGroups = 0; i < XkbNumKbdGroups; i++) {
        if ((xkb->compat->groups[i].real_mods != 0) ||
            (xkb->compat->groups[i].vmods != 0)) {
            groups |= (1 << i);
            nGroups++;
        }
    }
    info->group_compat = groups;
    info->num_group_compat = nGroups;
    size = 4;                   /* room for num_si and group_compat mask */
    size += xkmSizeCountedString(name);
    size += (SIZEOF(xkmSymInterpretDesc) * xkb->compat->num_si);
    size += (SIZEOF(xkmModsDesc) * nGroups);
    toc->type = XkmCompatMapIndex;
    toc->format = MSBFirst;
    toc->size = size + SIZEOF(xkmSectionInfo);
    toc->offset = (*offset_inout);
    (*offset_inout) += toc->size;
    return 1;
}

static unsigned
WriteXKMCompatMap(FILE *file, XkbFileInfo *result, XkmInfo *info)
{
    register unsigned i;
    char *name;
    XkbDescPtr xkb;
    XkbSymInterpretPtr interp;
    xkmSymInterpretDesc wire;
    Display *dpy;
    unsigned tmp, size = 0;

    xkb = result->xkb;
    dpy = xkb->dpy;
    if (xkb->names)
        name = XkbAtomGetString(dpy, xkb->names->compat);
    else
        name = NULL;
    size += xkmPutCountedString(file, name);
    size += xkmPutCARD16(file, xkb->compat->num_si);
    size += xkmPutCARD8(file, info->group_compat);
    size += xkmPutPadding(file, 1);
    interp = xkb->compat->sym_interpret;
    for (i = 0; i < xkb->compat->num_si; i++, interp++) {
        wire.sym = interp->sym;
        wire.mods = interp->mods;
        wire.match = interp->match;
        wire.virtualMod = interp->virtual_mod;
        wire.flags = interp->flags;
        wire.actionType = interp->act.type;
        wire.actionData[0] = interp->act.data[0];
        wire.actionData[1] = interp->act.data[1];
        wire.actionData[2] = interp->act.data[2];
        wire.actionData[3] = interp->act.data[3];
        wire.actionData[4] = interp->act.data[4];
        wire.actionData[5] = interp->act.data[5];
        wire.actionData[6] = interp->act.data[6];
        tmp = fwrite(&wire, SIZEOF(xkmSymInterpretDesc), 1, file);
        size += tmp * SIZEOF(xkmSymInterpretDesc);
    }
    if (info->group_compat) {
        register unsigned bit;

        xkmModsDesc modsWire;

        for (i = 0, bit = 1; i < XkbNumKbdGroups; i++, bit <<= 1) {
            if (info->group_compat & bit) {
                modsWire.realMods = xkb->compat->groups[i].real_mods;
                modsWire.virtualMods = xkb->compat->groups[i].vmods;
		modsWire.pad = 0;
                fwrite(&modsWire, SIZEOF(xkmModsDesc), 1, file);
                size += SIZEOF(xkmModsDesc);
            }
        }
    }
    return size;
}

/***====================================================================***/

static unsigned
SizeXKMSymbols(XkbFileInfo *result, XkmInfo *info,
               xkmSectionInfo *toc, int *offset_inout)
{
    Display *dpy;
    XkbDescPtr xkb;
    unsigned size;
    register int i, nSyms;
    char *name;

    xkb = result->xkb;
    if ((!xkb) || (!xkb->map) || ((!xkb->map->syms))) {
        _XkbLibError(_XkbErrMissingSymbols, "SizeXKMSymbols", 0);
        return 0;
    }
    dpy = xkb->dpy;
    if (xkb->names && (xkb->names->symbols != None))
        name = XkbAtomGetString(dpy, xkb->names->symbols);
    else
        name = NULL;
    size = xkmSizeCountedString(name);
    size += 4;                  /* min and max keycode, group names mask */
    for (i = 0; i < XkbNumKbdGroups; i++) {
        if (xkb->names->groups[i] != None)
            size += xkmSizeCountedAtomString(dpy, xkb->names->groups[i]);
    }
    info->total_vmodmaps = 0;
    for (i = xkb->min_key_code; i <= (int) xkb->max_key_code; i++) {
        nSyms = XkbKeyNumSyms(xkb, i);
        size += SIZEOF(xkmKeySymMapDesc) + (nSyms * 4);
        if (xkb->server) {
            if (xkb->server->explicit[i] & XkbExplicitKeyTypesMask) {
                register int g;

                for (g = XkbKeyNumGroups(xkb, i) - 1; g >= 0; g--) {
                    if (xkb->server->explicit[i] & (1 << g)) {
                        XkbKeyTypePtr type;
                        char *name;

                        type = XkbKeyKeyType(xkb, i, g);
                        name = XkbAtomGetString(dpy, type->name);
                        if (name != NULL)
                            size += xkmSizeCountedString(name);
                    }
                }
            }
            if (XkbKeyHasActions(xkb, i))
                size += nSyms * SIZEOF(xkmActionDesc);
            if (xkb->server->behaviors[i].type != XkbKB_Default)
                size += SIZEOF(xkmBehaviorDesc);
            if (xkb->server->vmodmap && (xkb->server->vmodmap[i] != 0))
                info->total_vmodmaps++;
        }
    }
    size += info->total_vmodmaps * SIZEOF(xkmVModMapDesc);
    toc->type = XkmSymbolsIndex;
    toc->format = MSBFirst;
    toc->size = size + SIZEOF(xkmSectionInfo);
    toc->offset = (*offset_inout);
    (*offset_inout) += toc->size;
    return 1;
}

static unsigned
WriteXKMSymbols(FILE *file, XkbFileInfo *result, XkmInfo *info)
{
    Display *dpy;
    XkbDescPtr xkb;
    register int i, n;
    xkmKeySymMapDesc wireMap;
    char *name;
    unsigned tmp, size = 0;

    xkb = result->xkb;
    dpy = xkb->dpy;
    if (xkb->names && (xkb->names->symbols != None))
        name = XkbAtomGetString(dpy, xkb->names->symbols);
    else
        name = NULL;
    size += xkmPutCountedString(file, name);
    for (tmp = i = 0; i < XkbNumKbdGroups; i++) {
        if (xkb->names->groups[i] != None)
            tmp |= (1 << i);
    }
    size += xkmPutCARD8(file, xkb->min_key_code);
    size += xkmPutCARD8(file, xkb->max_key_code);
    size += xkmPutCARD8(file, tmp);
    size += xkmPutCARD8(file, info->total_vmodmaps);
    for (i = 0, n = 1; i < XkbNumKbdGroups; i++, n <<= 1) {
        if ((tmp & n) == 0)
            continue;
        size += xkmPutCountedAtomString(dpy, file, xkb->names->groups[i]);
    }
    for (i = xkb->min_key_code; i <= (int) xkb->max_key_code; i++) {
        char *typeName[XkbNumKbdGroups];

        wireMap.width = XkbKeyGroupsWidth(xkb, i);
        wireMap.num_groups = XkbKeyGroupInfo(xkb, i);
        if (xkb->map && xkb->map->modmap)
            wireMap.modifier_map = xkb->map->modmap[i];
        else
            wireMap.modifier_map = 0;
        wireMap.flags = 0;
        bzero((char *) typeName, XkbNumKbdGroups * sizeof(char *));
        if (xkb->server) {
            if (xkb->server->explicit[i] & XkbExplicitKeyTypesMask) {
                register int g;

                for (g = 0; g < XkbKeyNumGroups(xkb, i); g++) {
                    if (xkb->server->explicit[i] & (1 << g)) {
                        XkbKeyTypePtr type;

                        type = XkbKeyKeyType(xkb, i, g);
                        typeName[g] = XkbAtomGetString(dpy, type->name);
                        if (typeName[g] != NULL)
                            wireMap.flags |= (1 << g);
                    }
                }
            }
            if (XkbKeyHasActions(xkb, i))
                wireMap.flags |= XkmKeyHasActions;
            if (xkb->server->behaviors[i].type != XkbKB_Default)
                wireMap.flags |= XkmKeyHasBehavior;
            if ((xkb->server->explicit[i] & XkbExplicitAutoRepeatMask) &&
                (xkb->ctrls != NULL)) {
                if (xkb->ctrls->per_key_repeat[(i / 8)] & (1 << (i % 8)))
                    wireMap.flags |= XkmRepeatingKey;
                else
                    wireMap.flags |= XkmNonRepeatingKey;
            }
        }
        tmp = fwrite(&wireMap, SIZEOF(xkmKeySymMapDesc), 1, file);
        size += tmp * SIZEOF(xkmKeySymMapDesc);
        if (xkb->server->explicit[i] & XkbExplicitKeyTypesMask) {
            register int g;

            for (g = 0; g < XkbNumKbdGroups; g++) {
                if (typeName[g] != NULL)
                    size += xkmPutCountedString(file, typeName[g]);
            }
        }
        if (XkbNumGroups(wireMap.num_groups) > 0) {
            KeySym *sym;

            sym = XkbKeySymsPtr(xkb, i);
            for (n = XkbKeyNumSyms(xkb, i); n > 0; n--, sym++) {
                size += xkmPutCARD32(file, (CARD32) *sym);
            }
            if (wireMap.flags & XkmKeyHasActions) {
                XkbAction *act;

                act = XkbKeyActionsPtr(xkb, i);
                for (n = XkbKeyNumActions(xkb, i); n > 0; n--, act++) {
                    tmp = fwrite(act, SIZEOF(xkmActionDesc), 1, file);
                    size += tmp * SIZEOF(xkmActionDesc);
                }
            }
        }
        if (wireMap.flags & XkmKeyHasBehavior) {
            xkmBehaviorDesc b;

            b.type = xkb->server->behaviors[i].type;
            b.data = xkb->server->behaviors[i].data;
            tmp = fwrite(&b, SIZEOF(xkmBehaviorDesc), 1, file);
            size += tmp * SIZEOF(xkmBehaviorDesc);
        }
    }
    if (info->total_vmodmaps > 0) {
        xkmVModMapDesc v;

        for (i = xkb->min_key_code; i <= xkb->max_key_code; i++) {
            if (xkb->server->vmodmap[i] != 0) {
                v.key = i;
                v.vmods = xkb->server->vmodmap[i];
                tmp = fwrite(&v, SIZEOF(xkmVModMapDesc), 1, file);
                size += tmp * SIZEOF(xkmVModMapDesc);
            }
        }
    }
    return size;
}

/***====================================================================***/

static unsigned
SizeXKMIndicators(XkbFileInfo *result, XkmInfo *info,
                  xkmSectionInfo *toc, int *offset_inout)
{
    Display *dpy;
    XkbDescPtr xkb;
    unsigned size;
    register unsigned i, nLEDs;

    xkb = result->xkb;
    if ((xkb == NULL) || (xkb->indicators == NULL)) {
/*	_XkbLibError(_XkbErrMissingIndicators,"SizeXKMIndicators",0);*/
        return 0;
    }
    dpy = xkb->dpy;
    nLEDs = 0;
    size = 8; /* number of indicator maps/physical indicators */
    if (xkb->indicators != NULL) {
        for (i = 0; i < XkbNumIndicators; i++) {
            XkbIndicatorMapPtr map = &xkb->indicators->maps[i];

            if ((map->flags != 0) || (map->which_groups != 0) ||
                (map->groups != 0) || (map->which_mods != 0) ||
                (map->mods.real_mods != 0) || (map->mods.vmods != 0) ||
                (map->ctrls != 0) ||
                (xkb->names && (xkb->names->indicators[i] != None))) {
                char *name;

                if (xkb->names && xkb->names->indicators[i] != None) {
                    name = XkbAtomGetString(dpy, xkb->names->indicators[i]);
                }
                else
                    name = NULL;
                size += xkmSizeCountedString(name);
                size += SIZEOF(xkmIndicatorMapDesc);
                nLEDs++;
            }
        }
    }
    info->num_leds = nLEDs;
    toc->type = XkmIndicatorsIndex;
    toc->format = MSBFirst;
    toc->size = size + SIZEOF(xkmSectionInfo);
    toc->offset = (*offset_inout);
    (*offset_inout) += toc->size;
    return 1;
}

static unsigned
WriteXKMIndicators(FILE *file, XkbFileInfo *result, XkmInfo *info)
{
    Display *dpy;
    XkbDescPtr xkb;
    register unsigned i;
    xkmIndicatorMapDesc wire;
    unsigned tmp, size = 0;

    xkb = result->xkb;
    dpy = xkb->dpy;
    size += xkmPutCARD8(file, info->num_leds);
    size += xkmPutPadding(file, 3);
    size += xkmPutCARD32(file, xkb->indicators->phys_indicators);
    if (xkb->indicators != NULL) {
        for (i = 0; i < XkbNumIndicators; i++) {
            XkbIndicatorMapPtr map = &xkb->indicators->maps[i];

            if ((map->flags != 0) || (map->which_groups != 0) ||
                (map->groups != 0) || (map->which_mods != 0) ||
                (map->mods.real_mods != 0) || (map->mods.vmods != 0) ||
                (map->ctrls != 0) || (xkb->names &&
                                      (xkb->names->indicators[i] != None))) {
                char *name;

                if (xkb->names && xkb->names->indicators[i] != None) {
                    name = XkbAtomGetString(dpy, xkb->names->indicators[i]);
                }
                else
                    name = NULL;
                size += xkmPutCountedString(file, name);
                wire.indicator = i + 1;
                wire.flags = map->flags;
                wire.which_mods = map->which_mods;
                wire.real_mods = map->mods.real_mods;
                wire.vmods = map->mods.vmods;
                wire.which_groups = map->which_groups;
                wire.groups = map->groups;
                wire.ctrls = map->ctrls;
                tmp = fwrite(&wire, SIZEOF(xkmIndicatorMapDesc), 1, file);
                size += tmp * SIZEOF(xkmIndicatorMapDesc);
            }
        }
    }
    return size;
}

/***====================================================================***/

static unsigned
SizeXKMGeomDoodad(XkbFileInfo *result, XkbDoodadPtr doodad)
{
    unsigned size;

    size = SIZEOF(xkmAnyDoodadDesc);
    size += xkmSizeCountedAtomString(result->xkb->dpy, doodad->any.name);
    if (doodad->any.type == XkbTextDoodad) {
        size += xkmSizeCountedString(doodad->text.text);
        size += xkmSizeCountedString(doodad->text.font);
    }
    else if (doodad->any.type == XkbLogoDoodad) {
        size += xkmSizeCountedString(doodad->logo.logo_name);
    }
    return size;
}

static unsigned
SizeXKMGeomSection(XkbFileInfo *result, XkbSectionPtr section)
{
    register int i;
    unsigned size;

    size = SIZEOF(xkmSectionDesc);
    size += xkmSizeCountedAtomString(result->xkb->dpy, section->name);
    if (section->rows) {
        XkbRowPtr row;

        for (row = section->rows, i = 0; i < section->num_rows; i++, row++) {
            size += SIZEOF(xkmRowDesc);
            size += row->num_keys * SIZEOF(xkmKeyDesc);
        }
    }
    if (section->doodads) {
        XkbDoodadPtr doodad;

        for (doodad = section->doodads, i = 0; i < section->num_doodads;
             i++, doodad++) {
            size += SizeXKMGeomDoodad(result, doodad);
        }
    }
    if (section->overlays) {
        XkbOverlayPtr ol;

        for (ol = section->overlays, i = 0; i < section->num_overlays;
             i++, ol++) {
            register int r;
            XkbOverlayRowPtr row;

            size += xkmSizeCountedAtomString(result->xkb->dpy, ol->name);
            size += SIZEOF(xkmOverlayDesc);
            for (r = 0, row = ol->rows; r < ol->num_rows; r++, row++) {
                size += SIZEOF(xkmOverlayRowDesc);
                size += row->num_keys * SIZEOF(xkmOverlayKeyDesc);
            }
        }
    }
    return size;
}

static unsigned
SizeXKMGeometry(XkbFileInfo *result, xkmSectionInfo *toc, int *offset_inout)
{
    register int i;
    Display *dpy;
    XkbDescPtr xkb;
    XkbGeometryPtr geom;
    unsigned size;

    xkb = result->xkb;
    if ((!xkb) || (!xkb->geom))
        return 0;
    dpy = xkb->dpy;
    geom = xkb->geom;
    size = xkmSizeCountedAtomString(dpy, geom->name);
    size += SIZEOF(xkmGeometryDesc);
    size += xkmSizeCountedString(geom->label_font);
    if (geom->properties) {
        XkbPropertyPtr prop;

        for (i = 0, prop = geom->properties; i < geom->num_properties;
             i++, prop++) {
            size += xkmSizeCountedString(prop->name);
            size += xkmSizeCountedString(prop->value);
        }
    }
    if (geom->colors) {
        XkbColorPtr color;

        for (i = 0, color = geom->colors; i < geom->num_colors; i++, color++) {
            size += xkmSizeCountedString(color->spec);
        }
    }
    if (geom->shapes) {
        XkbShapePtr shape;

        for (i = 0, shape = geom->shapes; i < geom->num_shapes; i++, shape++) {
            register int n;
            register XkbOutlinePtr ol;

            size += xkmSizeCountedAtomString(dpy, shape->name);
            size += SIZEOF(xkmShapeDesc);
            for (n = 0, ol = shape->outlines; n < shape->num_outlines;
                 n++, ol++) {
                size += SIZEOF(xkmOutlineDesc);
                size += ol->num_points * SIZEOF(xkmPointDesc);
            }
        }
    }
    if (geom->sections) {
        XkbSectionPtr section;

        for (i = 0, section = geom->sections; i < geom->num_sections;
             i++, section++) {
            size += SizeXKMGeomSection(result, section);
        }
    }
    if (geom->doodads) {
        XkbDoodadPtr doodad;

        for (i = 0, doodad = geom->doodads; i < geom->num_doodads;
             i++, doodad++) {
            size += SizeXKMGeomDoodad(result, doodad);
        }
    }
    if (geom->key_aliases) {
        size += geom->num_key_aliases * (XkbKeyNameLength * 2);
    }
    toc->type = XkmGeometryIndex;
    toc->format = MSBFirst;
    toc->size = size + SIZEOF(xkmSectionInfo);
    toc->offset = (*offset_inout);
    (*offset_inout) += toc->size;
    return 1;
}

static unsigned
WriteXKMGeomDoodad(FILE *file, XkbFileInfo *result, XkbDoodadPtr doodad)
{
    Display *dpy;
    XkbDescPtr xkb;
    xkmDoodadDesc doodadWire;
    unsigned tmp, size = 0;

    xkb = result->xkb;
    dpy = xkb->dpy;
    bzero((char *) &doodadWire, sizeof(doodadWire));
    doodadWire.any.type = doodad->any.type;
    doodadWire.any.priority = doodad->any.priority;
    doodadWire.any.top = doodad->any.top;
    doodadWire.any.left = doodad->any.left;
    switch (doodad->any.type) {
    case XkbOutlineDoodad:
    case XkbSolidDoodad:
        doodadWire.shape.angle = doodad->shape.angle;
        doodadWire.shape.color_ndx = doodad->shape.color_ndx;
        doodadWire.shape.shape_ndx = doodad->shape.shape_ndx;
        break;
    case XkbTextDoodad:
        doodadWire.text.angle = doodad->text.angle;
        doodadWire.text.width = doodad->text.width;
        doodadWire.text.height = doodad->text.height;
        doodadWire.text.color_ndx = doodad->text.color_ndx;
        break;
    case XkbIndicatorDoodad:
        doodadWire.indicator.shape_ndx = doodad->indicator.shape_ndx;
        doodadWire.indicator.on_color_ndx = doodad->indicator.on_color_ndx;
        doodadWire.indicator.off_color_ndx = doodad->indicator.off_color_ndx;
        break;
    case XkbLogoDoodad:
        doodadWire.logo.angle = doodad->logo.angle;
        doodadWire.logo.color_ndx = doodad->logo.color_ndx;
        doodadWire.logo.shape_ndx = doodad->logo.shape_ndx;
        break;
    default:
        _XkbLibError(_XkbErrIllegalDoodad, "WriteXKMGeomDoodad",
                     doodad->any.type);
        return 0;
    }
    size += xkmPutCountedAtomString(dpy, file, doodad->any.name);
    tmp = fwrite(&doodadWire, SIZEOF(xkmDoodadDesc), 1, file);
    size += tmp * SIZEOF(xkmDoodadDesc);
    if (doodad->any.type == XkbTextDoodad) {
        size += xkmPutCountedString(file, doodad->text.text);
        size += xkmPutCountedString(file, doodad->text.font);
    }
    else if (doodad->any.type == XkbLogoDoodad) {
        size += xkmPutCountedString(file, doodad->logo.logo_name);
    }
    return size;
}

static unsigned
WriteXKMGeomOverlay(FILE *file, XkbFileInfo *result, XkbOverlayPtr ol)
{
    register int r, k;
    Display *dpy;
    XkbDescPtr xkb;
    XkbOverlayRowPtr row;
    xkmOverlayDesc olWire;
    xkmOverlayRowDesc rowWire;
    xkmOverlayKeyDesc keyWire;
    unsigned tmp, size = 0;

    xkb = result->xkb;
    dpy = xkb->dpy;
    bzero((char *) &olWire, sizeof(olWire));
    bzero((char *) &rowWire, sizeof(rowWire));
    bzero((char *) &keyWire, sizeof(keyWire));
    size += xkmPutCountedAtomString(dpy, file, ol->name);
    olWire.num_rows = ol->num_rows;
    tmp = fwrite(&olWire, SIZEOF(xkmOverlayDesc), 1, file);
    size += tmp * SIZEOF(xkmOverlayDesc);
    for (r = 0, row = ol->rows; r < ol->num_rows; r++, row++) {
        XkbOverlayKeyPtr key;

        rowWire.row_under = row->row_under;
        rowWire.num_keys = row->num_keys;
        tmp = fwrite(&rowWire, SIZEOF(xkmOverlayRowDesc), 1, file);
        size += tmp * SIZEOF(xkmOverlayRowDesc);
        for (k = 0, key = row->keys; k < row->num_keys; k++, key++) {
            memcpy(keyWire.over, key->over.name, XkbKeyNameLength);
            memcpy(keyWire.under, key->under.name, XkbKeyNameLength);
            tmp = fwrite(&keyWire, SIZEOF(xkmOverlayKeyDesc), 1, file);
            size += tmp * SIZEOF(xkmOverlayKeyDesc);
        }
    }
    return size;
}

static unsigned
WriteXKMGeomSection(FILE *file, XkbFileInfo *result, XkbSectionPtr section)
{
    register int i;
    Display *dpy;
    XkbDescPtr xkb;
    xkmSectionDesc sectionWire;
    unsigned tmp, size = 0;

    xkb = result->xkb;
    dpy = xkb->dpy;
    size += xkmPutCountedAtomString(dpy, file, section->name);
    sectionWire.top = section->top;
    sectionWire.left = section->left;
    sectionWire.width = section->width;
    sectionWire.height = section->height;
    sectionWire.angle = section->angle;
    sectionWire.priority = section->priority;
    sectionWire.num_rows = section->num_rows;
    sectionWire.num_doodads = section->num_doodads;
    sectionWire.num_overlays = section->num_overlays;
    sectionWire.pad2 = 0;
    tmp = fwrite(&sectionWire, SIZEOF(xkmSectionDesc), 1, file);
    size += tmp * SIZEOF(xkmSectionDesc);
    if (section->rows) {
        register unsigned k;
        XkbRowPtr row;
        xkmRowDesc rowWire;
        XkbKeyPtr key;
        xkmKeyDesc keyWire;

        for (i = 0, row = section->rows; i < section->num_rows; i++, row++) {
            rowWire.top = row->top;
            rowWire.left = row->left;
            rowWire.num_keys = row->num_keys;
            rowWire.vertical = row->vertical;
			rowWire.pad = 0;
            tmp = fwrite(&rowWire, SIZEOF(xkmRowDesc), 1, file);
            size += tmp * SIZEOF(xkmRowDesc);
            for (k = 0, key = row->keys; k < row->num_keys; k++, key++) {
                memcpy(keyWire.name, key->name.name, XkbKeyNameLength);
                keyWire.gap = key->gap;
                keyWire.shape_ndx = key->shape_ndx;
                keyWire.color_ndx = key->color_ndx;
                tmp = fwrite(&keyWire, SIZEOF(xkmKeyDesc), 1, file);
                size += tmp * SIZEOF(xkmKeyDesc);
            }
        }
    }
    if (section->doodads) {
        XkbDoodadPtr doodad;

        for (i = 0, doodad = section->doodads; i < section->num_doodads;
             i++, doodad++) {
            size += WriteXKMGeomDoodad(file, result, doodad);
        }
    }
    if (section->overlays) {
        XkbOverlayPtr ol;

        for (i = 0, ol = section->overlays; i < section->num_overlays;
             i++, ol++) {
            size += WriteXKMGeomOverlay(file, result, ol);
        }
    }
    return size;
}

static unsigned
WriteXKMGeometry(FILE *file, XkbFileInfo *result)
{
    register int i;
    Display *dpy;
    XkbDescPtr xkb;
    XkbGeometryPtr geom;
    xkmGeometryDesc wire;
    unsigned tmp, size = 0;

    xkb = result->xkb;
    if ((!xkb) || (!xkb->geom))
        return 0;
    dpy = xkb->dpy;
    geom = xkb->geom;
    wire.width_mm = geom->width_mm;
    wire.height_mm = geom->height_mm;
    wire.base_color_ndx = XkbGeomColorIndex(geom, geom->base_color);
    wire.label_color_ndx = XkbGeomColorIndex(geom, geom->label_color);
    wire.num_properties = geom->num_properties;
    wire.num_colors = geom->num_colors;
    wire.num_shapes = geom->num_shapes;
    wire.num_sections = geom->num_sections;
    wire.num_doodads = geom->num_doodads;
    wire.num_key_aliases = geom->num_key_aliases;
    wire.pad1 = 0;
    size += xkmPutCountedAtomString(dpy, file, geom->name);
    tmp = fwrite(&wire, SIZEOF(xkmGeometryDesc), 1, file);
    size += tmp * SIZEOF(xkmGeometryDesc);
    size += xkmPutCountedString(file, geom->label_font);
    if (geom->properties) {
        XkbPropertyPtr prop;

        for (i = 0, prop = geom->properties; i < geom->num_properties;
             i++, prop++) {
            size += xkmPutCountedString(file, prop->name);
            size += xkmPutCountedString(file, prop->value);
        }
    }
    if (geom->colors) {
        XkbColorPtr color;

        for (i = 0, color = geom->colors; i < geom->num_colors; i++, color++) {
            size += xkmPutCountedString(file, color->spec);
        }
    }
    if (geom->shapes) {
        XkbShapePtr shape;
        xkmShapeDesc shapeWire;

        for (i = 0, shape = geom->shapes; i < geom->num_shapes; i++, shape++) {
            register int n;
            XkbOutlinePtr ol;
            xkmOutlineDesc olWire;

            bzero((char *) &shapeWire, sizeof(xkmShapeDesc));
            size += xkmPutCountedAtomString(dpy, file, shape->name);
            shapeWire.num_outlines = shape->num_outlines;
            if (shape->primary != NULL)
                shapeWire.primary_ndx = XkbOutlineIndex(shape, shape->primary);
            else
                shapeWire.primary_ndx = XkbNoShape;
            if (shape->approx != NULL)
                shapeWire.approx_ndx = XkbOutlineIndex(shape, shape->approx);
            else
                shapeWire.approx_ndx = XkbNoShape;
            tmp = fwrite(&shapeWire, SIZEOF(xkmShapeDesc), 1, file);
            size += tmp * SIZEOF(xkmShapeDesc);
            for (n = 0, ol = shape->outlines; n < shape->num_outlines;
                 n++, ol++) {
                register int p;
                XkbPointPtr pt;
                xkmPointDesc ptWire;

                olWire.num_points = ol->num_points;
                olWire.corner_radius = ol->corner_radius;
		olWire.pad = 0;
                tmp = fwrite(&olWire, SIZEOF(xkmOutlineDesc), 1, file);
                size += tmp * SIZEOF(xkmOutlineDesc);
                for (p = 0, pt = ol->points; p < ol->num_points; p++, pt++) {
                    ptWire.x = pt->x;
                    ptWire.y = pt->y;
                    tmp = fwrite(&ptWire, SIZEOF(xkmPointDesc), 1, file);
                    size += tmp * SIZEOF(xkmPointDesc);
                }
            }
        }
    }
    if (geom->sections) {
        XkbSectionPtr section;

        for (i = 0, section = geom->sections; i < geom->num_sections;
             i++, section++) {
            size += WriteXKMGeomSection(file, result, section);
        }
    }
    if (geom->doodads) {
        XkbDoodadPtr doodad;

        for (i = 0, doodad = geom->doodads; i < geom->num_doodads;
             i++, doodad++) {
            size += WriteXKMGeomDoodad(file, result, doodad);
        }
    }
    if (geom->key_aliases) {
        tmp =
            fwrite(geom->key_aliases, 2 * XkbKeyNameLength,
                   geom->num_key_aliases, file);
        size += tmp * (2 * XkbKeyNameLength);
    }
    return size;
}

/***====================================================================***/

/*ARGSUSED*/
static int
GetXKMKeyNamesTOC(XkbFileInfo *result, XkmInfo *info,
                  int max_toc, xkmSectionInfo *toc_rtrn)
{
    int num_toc;
    int total_size;

    total_size = num_toc = 0;
    if (SizeXKMKeycodes(result, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    if (SizeXKMIndicators(result, info, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    return num_toc;
}

/*ARGSUSED*/
static int
GetXKMTypesTOC(XkbFileInfo *result, XkmInfo *info,
               int max_toc, xkmSectionInfo *toc_rtrn)
{
    int num_toc;
    int total_size;

    total_size = num_toc = 0;
    if (SizeXKMVirtualMods(result, info, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    if (SizeXKMKeyTypes(result, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    return num_toc;
}

/*ARGSUSED*/
static int
GetXKMCompatMapTOC(XkbFileInfo *result, XkmInfo *info,
                   int max_toc, xkmSectionInfo *toc_rtrn)
{
    int num_toc;
    int total_size;

    total_size = num_toc = 0;
    if (SizeXKMVirtualMods(result, info, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    if (SizeXKMCompatMap(result, info, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    if (SizeXKMIndicators(result, info, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    return num_toc;
}

/*ARGSUSED*/
static int
GetXKMSemanticsTOC(XkbFileInfo *result, XkmInfo *info,
                   int max_toc, xkmSectionInfo *toc_rtrn)
{
    int num_toc;
    int total_size;

    total_size = num_toc = 0;
    if (SizeXKMVirtualMods(result, info, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    if (SizeXKMKeyTypes(result, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    if (SizeXKMCompatMap(result, info, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    if (SizeXKMIndicators(result, info, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    return num_toc;
}

/*ARGSUSED*/
static int
GetXKMLayoutTOC(XkbFileInfo *result, XkmInfo *info,
                int max_toc, xkmSectionInfo *toc_rtrn)
{
    int num_toc;
    int total_size;

    total_size = num_toc = 0;
    if (SizeXKMVirtualMods(result, info, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    if (SizeXKMKeycodes(result, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    if (SizeXKMKeyTypes(result, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    if (SizeXKMSymbols(result, info, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    if (SizeXKMIndicators(result, info, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    if (SizeXKMGeometry(result, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    return num_toc;
}

/*ARGSUSED*/
static int
GetXKMKeymapTOC(XkbFileInfo *result, XkmInfo *info,
                int max_toc, xkmSectionInfo *toc_rtrn)
{
    int num_toc;
    int total_size;

    total_size = num_toc = 0;
    if (SizeXKMVirtualMods(result, info, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    if (SizeXKMKeycodes(result, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    if (SizeXKMKeyTypes(result, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    if (SizeXKMCompatMap(result, info, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    if (SizeXKMSymbols(result, info, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    if (SizeXKMIndicators(result, info, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    if (SizeXKMGeometry(result, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    return num_toc;
}

/*ARGSUSED*/
static int
GetXKMGeometryTOC(XkbFileInfo *result, XkmInfo *info,
                  int max_toc, xkmSectionInfo *toc_rtrn)
{
    int num_toc;
    int total_size;

    total_size = num_toc = 0;
    if (SizeXKMGeometry(result, &toc_rtrn[num_toc], &total_size))
        num_toc++;
    return num_toc;
}

static Bool
WriteXKMFile(FILE *file, XkbFileInfo *result,
             int num_toc, xkmSectionInfo *toc, XkmInfo *info)
{
    register int i;
    unsigned tmp, size, total = 0;

    for (i = 0; i < num_toc; i++) {
        tmp = fwrite(&toc[i], SIZEOF(xkmSectionInfo), 1, file);
        total += tmp * SIZEOF(xkmSectionInfo);
        switch (toc[i].type) {
        case XkmTypesIndex:
            size = WriteXKMKeyTypes(file, result);
            break;
        case XkmCompatMapIndex:
            size = WriteXKMCompatMap(file, result, info);
            break;
        case XkmSymbolsIndex:
            size = WriteXKMSymbols(file, result, info);
            break;
        case XkmIndicatorsIndex:
            size = WriteXKMIndicators(file, result, info);
            break;
        case XkmKeyNamesIndex:
            size = WriteXKMKeycodes(file, result);
            break;
        case XkmGeometryIndex:
            size = WriteXKMGeometry(file, result);
            break;
        case XkmVirtualModsIndex:
            size = WriteXKMVirtualMods(file, result, info);
            break;
        default:
            _XkbLibError(_XkbErrIllegalTOCType, "WriteXKMFile", toc[i].type);
            return False;
        }
        size += SIZEOF(xkmSectionInfo);
        if (size != toc[i].size) {
            _XkbLibError(_XkbErrBadLength,
                         XkbConfigText(toc[i].type, XkbMessage),
                         size - toc[i].size);
            return False;
        }
    }
    return True;
}


#define	MAX_TOC	16

Bool
XkbWriteXKMFile(FILE *out, XkbFileInfo *result)
{
    Bool ok;
    XkbDescPtr xkb;
    XkmInfo info;
    int size_toc, i;
    unsigned hdr, present;
    xkmFileInfo fileInfo;
    xkmSectionInfo toc[MAX_TOC];

    int (*getTOC) (XkbFileInfo *        /* result */ ,
                   XkmInfo *            /* info */ ,
                   int                  /* max_to */ ,
                   xkmSectionInfo *     /* toc_rtrn */
        );

    switch (result->type) {
    case XkmKeyNamesIndex:
        getTOC = GetXKMKeyNamesTOC;
        break;
    case XkmTypesIndex:
        getTOC = GetXKMTypesTOC;
        break;
    case XkmCompatMapIndex:
        getTOC = GetXKMCompatMapTOC;
        break;
    case XkmSemanticsFile:
        getTOC = GetXKMSemanticsTOC;
        break;
    case XkmLayoutFile:
        getTOC = GetXKMLayoutTOC;
        break;
    case XkmKeymapFile:
        getTOC = GetXKMKeymapTOC;
        break;
    case XkmGeometryFile:
    case XkmGeometryIndex:
        getTOC = GetXKMGeometryTOC;
        break;
    default:
        _XkbLibError(_XkbErrIllegalContents,
                     XkbConfigText(result->type, XkbMessage), 0);
        return False;
    }
    xkb = result->xkb;

    bzero((char *) &info, sizeof(XkmInfo));
    size_toc = (*getTOC) (result, &info, MAX_TOC, toc);
    if (size_toc < 1) {
        _XkbLibError(_XkbErrEmptyFile, "XkbWriteXKMFile", 0);
        return False;
    }
    if (out == NULL) {
        _XkbLibError(_XkbErrFileCannotOpen, "XkbWriteXKMFile", 0);
        return False;
    }
    for (i = present = 0; i < size_toc; i++) {
        toc[i].offset += 4 + SIZEOF(xkmFileInfo);
        toc[i].offset += (size_toc * SIZEOF(xkmSectionInfo));
        if (toc[i].type <= XkmLastIndex) {
            present |= (1 << toc[i].type);
        }
#ifdef DEBUG
        else {
            fprintf(stderr, "Illegal section type %d\n", toc[i].type);
            fprintf(stderr, "Ignored\n");
        }
#endif
    }
    hdr = (('x' << 24) | ('k' << 16) | ('m' << 8) | XkmFileVersion);
    xkmPutCARD32(out, (unsigned long) hdr);
    fileInfo.type = result->type;
    fileInfo.min_kc = xkb->min_key_code;
    fileInfo.max_kc = xkb->max_key_code;
    fileInfo.num_toc = size_toc;
    fileInfo.present = present;
    fileInfo.pad = 0;
    fwrite(&fileInfo, SIZEOF(xkmFileInfo), 1, out);
    fwrite(toc, SIZEOF(xkmSectionInfo), size_toc, out);
    ok = WriteXKMFile(out, result, size_toc, toc, &info);
    return ok;
}
